// Copyright 2007 the original author or authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package lichen.services;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.Properties;
import java.util.Map.Entry;

import javax.persistence.Entity;

import lichen.entities.PersistModel;
import lichen.entities.forum.Forum;
import lichen.entities.forum.ForumImpl;
import lichen.entities.user.User;
import lichen.services.binding.ListBindingFactory;
import lichen.services.forum.ForumClassService;
import lichen.services.forum.impl.ForumClassServiceImpl;
import lichen.services.job.CheckOnline;
import lichen.services.job.SchedulerFactoryProxy;
import lichen.services.menu.MenuService;
import lichen.services.menu.impl.MenuServiceImpl;
import lichen.services.post.impl.PostServiceImpl;
import lichen.services.user.UserService;
import lichen.services.user.impl.UserServiceImpl;
import lichen.validator.Email;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.tapestry.MarkupWriter;
import org.apache.tapestry.Validator;
import org.apache.tapestry.hibernate.HibernateSessionManager;
import org.apache.tapestry.hibernate.HibernateSessionSource;
import org.apache.tapestry.internal.services.ClassNameLocator;
import org.apache.tapestry.internal.services.PageMarkupRenderer;
import org.apache.tapestry.internal.services.PageResponseRenderer;
import org.apache.tapestry.internal.structure.Page;
import org.apache.tapestry.ioc.Configuration;
import org.apache.tapestry.ioc.MappedConfiguration;
import org.apache.tapestry.ioc.OrderedConfiguration;
import org.apache.tapestry.ioc.ServiceBinder;
import org.apache.tapestry.ioc.annotations.Inject;
import org.apache.tapestry.ioc.annotations.InjectService;
import org.apache.tapestry.ioc.services.Coercion;
import org.apache.tapestry.ioc.services.CoercionTuple;
import org.apache.tapestry.services.ApplicationInitializer;
import org.apache.tapestry.services.ApplicationInitializerFilter;
import org.apache.tapestry.services.BindingFactory;
import org.apache.tapestry.services.BindingSource;
import org.apache.tapestry.services.Context;
import org.apache.tapestry.services.MarkupWriterFactory;
import org.apache.tapestry.services.Request;
import org.apache.tapestry.services.RequestFilter;
import org.apache.tapestry.services.RequestGlobals;
import org.apache.tapestry.services.RequestHandler;
import org.apache.tapestry.services.Response;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.TriggerUtils;

/**
 * 对tapestry的扩展
 * @author <a href="mailto:jun.tsai@gmail.com">Jun Tsai</a>
 * @version $Revision: 7 $
 * @since 0.0.1
 */
public class LichenModule {
	/** 页面采用的Content type **/
	protected static final String CONTENT_TYPE = "text/html; charset=UTF-8";
	/** 弹出的消息 **/
	private static final String VALIDATION__ZH_MESSAGES = "lichen/services/ValidationMessages_zh_CN";
	/** 系统启动时候的jvm配置参数,此参数决定是否初始化数据 **/
	private static final String LICHEN_ENV_NAME="lichen.env";
	/** 系统处于产品环境 **/
	private static final String LICHEN_ENV_PRODUCT="product";
	/** Job 中使用到的hibernate 的source **/
	public static final Object HIBERNATE_SESSION_SOURCE = "job-hibernate-session-source";
	/** lichen 的版本 **/
	public static final String LICHEN_VERSION_KEY_NAME = "lichen.version";
	

	/**
	 * 绑定使用的service.
	 * @param binder Service Binder
	 * @see ServiceBinder
	 */
	public static void bind(ServiceBinder binder) {
		binder.bind(ForumClassService.class, ForumClassServiceImpl.class);
		binder.bind(PostServiceImpl.class);
		binder.bind(UserService.class,UserServiceImpl.class);
		binder.bind(SchedulerFactory.class,SchedulerFactoryProxy.class);
		binder.bind(MenuService.class,MenuServiceImpl.class);
		
	}

	/**
	 * 页面解析采用utf-8方式.
	 * @param markupRenderer markup renderer
	 * @param markupWriterFactory writer factory
	 * @param delegate delegate 代理类
	 * @return page response renderer
	 */
	public static PageResponseRenderer decoratePageResponseRenderer(
			@InjectService("PageMarkupRenderer")
			final PageMarkupRenderer markupRenderer,
			@InjectService("MarkupWriterFactory")
			final MarkupWriterFactory markupWriterFactory, final Object delegate) {
		return new PageResponseRenderer() {
			public void renderPageResponse(Page page, Response response)
					throws IOException {
				MarkupWriter writer = markupWriterFactory.newMarkupWriter();
				markupRenderer.renderPageMarkup(page, writer);
				PrintWriter pw = response
						.getPrintWriter(CONTENT_TYPE);
				writer.toMarkup(pw);
				pw.flush();
			}
		};
	}
	 /**
	  * 表单输入也采用UTF-8信息
	  * @param configuration 配置.
	  * @param encodingFilter 编码的过滤器.
	  */
   public void contributeRequestHandler(  
           OrderedConfiguration<RequestFilter> configuration,  
           @InjectService("EncodingFilter")  
           RequestFilter encodingFilter) {  
       configuration.add("EncodingFilter", encodingFilter);  
   }  
	/**
	 * 加入中文的校验信息
	 * @param configuration 配置
	 */
    public void contributeValidationMessagesSource(Configuration<String> configuration)
    {
        configuration.add(VALIDATION__ZH_MESSAGES);
    }
    
	/**
	 * 初始化网站的数据
	 * @param configuration 配置
	 * @param session hibernate  session
	 * @param treeService tree service.
	 * @param manager hibernate session manager
	 */
	public void contributeApplicationInitializer(
			OrderedConfiguration<ApplicationInitializerFilter> configuration,
			@Inject
			final Session session, @Inject
			final ForumClassService treeService,
			@Inject
			final HibernateSessionManager manager) {
		//当环境为产品环境的时候，则不进行初始化一些数据
		if(LICHEN_ENV_PRODUCT.equalsIgnoreCase(System.getProperty(LICHEN_ENV_NAME))){
			return;
		}
		ApplicationInitializerFilter clearCaches = new ApplicationInitializerFilter() {
			public void initializeApplication(Context context,
					ApplicationInitializer initializer) {
				Forum category = new ForumImpl();
				category.setName("Java 技术");
				treeService.saveTreeChildNode(category, null, null);
				manager.commit();
				
				Transaction t = session.beginTransaction();
				Forum forum = new ForumImpl();
				forum.setName("J2SE");
				forum.setDescription("Java的基础知识");
				treeService.saveTreeChildNode(forum, category, null);
				t.commit();
				
				session.refresh(category);
				// 初始化一个User实体
				User user = new User();
				user.setEmail("asdf@asdf.net");
				user.setNickName("NICK NAME");
				user.setPassword(DigestUtils.md5Hex("asdf"));
				session.save(user);
				// transaction.commit();
				initializer.initializeApplication(context);
			}
		};
		configuration.add("initialDataBase", clearCaches);
	}

	/**
	 * 修改默认页面 为index
	 * 
	 * @param configuration            配置.
	 */
	public void contributeApplicationDefaults(
			MappedConfiguration<String, String> configuration) {
		//默认的配置页面
		configuration.add("tapestry.start-page-name", "index");
		//默认的样式
		configuration.add("lichen.skin", "default");
		//lichen 的版本
		Properties properties=new Properties();
		try {
			properties.load(LichenModule.class.getResourceAsStream("/version.properties"));
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		Iterator<Entry<Object, Object>> it = properties.entrySet().iterator();
		while(it.hasNext()){
			Entry<Object, Object> entry = it.next();
			configuration.add(entry.getKey().toString(),entry.getValue().toString());
		}
	}

	/**
	 * 对tapestry核心的TypeCorerce进行扩展，实现了PO到字符串的转换.
	 * @author <a href="mailto:jun.tsai@gmail.com">Jun Tsai</a>
	 * @version $Revision: 7 $
	 * @since 0.0.1
	 */
	public static class GenericCoercion<PO extends PersistModel> implements Coercion<String,PO>{
		private Session session;
		private Class<PO> clazz;
		public GenericCoercion(Session session,Class<PO> clazz){
			this.session=session;
			this.clazz=clazz;
		}
		@SuppressWarnings("unchecked")
		public PO coerce(String input) {
			return (PO) session.load(clazz, input);
		}
		
	}
	
	/**
	 * 构建TypeCoercer
	 * @param configuration 配置.
	 * @param session hibernate session.
	 * @param classNameLocator class name locator.
	 */
	@SuppressWarnings("unchecked")
	public static void contributeTypeCoercer(
			Configuration<CoercionTuple> configuration,
			@InjectService("Session")
			final Session session,@InjectService("ClassNameLocator")
		    ClassNameLocator classNameLocator) {
		
		String packageName="lichen.entities";
		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
		 for (String className : classNameLocator.locateClassNames(packageName))
         {
             try
             {
                 Class<?> entityClass = contextClassLoader.loadClass(className);
                 if(entityClass.isAnnotationPresent(Entity.class)){//仅仅处理有Entity注释的类
                	 configuration.add(new CoercionTuple(String.class,
                		 entityClass, new GenericCoercion(session,entityClass)));
                 }
             }
             catch (ClassNotFoundException ex)
             {
                 throw new RuntimeException(ex);
             }
         }
		classNameLocator.locateClassNames(packageName);
		Coercion<PersistModel, String> po2String = new Coercion<PersistModel, String>() {
			public String coerce(PersistModel input) {
				return session.getIdentifier(input).toString();
			}
		};

		configuration.add(new CoercionTuple<PersistModel, String>(
				PersistModel.class, String.class, po2String));
		
	}

	/**
	 * 扩展binding source,实现一个list的绑定.
	 * @param configuration 配置
	 * @param bindingSource binding source
	 * @see ListBindingFactory
	 */
	public static void contributeBindingSource(
            MappedConfiguration<String, BindingFactory> configuration,
            BindingSource bindingSource)
    {
        configuration.add("list",new ListBindingFactory(bindingSource));
    }

  
    /**
     * 创建一个编码的过滤器
     * @param requestGlobals 全局请求的Request.
     * @return 请求过滤器.
     */
    public RequestFilter buildEncodingFilter(final RequestGlobals requestGlobals) {  
        return new RequestFilter() {  
            public boolean service(Request request, Response response,  
                    RequestHandler handler) throws IOException {  
  
                requestGlobals.getHTTPServletRequest().setCharacterEncoding(  
                        "UTF-8");  
                return handler.service(request, response);  
            }  
        };  
    }   
    
    /**
     * 构建检查在线人数的服务类
     * @param sessionSource hibernate Session Source
     * @return check online job detail
     * @see HibernateSessionSource
     */
    public JobDetail buildCheckOnlineJob(@Inject HibernateSessionSource sessionSource){
    	JobDetail jobDetail = new JobDetail("check-online-job", "lichen", CheckOnline.class);
		jobDetail.getJobDataMap().put(HIBERNATE_SESSION_SOURCE,sessionSource);
		return jobDetail;
    }
    /**
     * 初始化所有的job项目
     * @param configuration configuration
     * @param factory scheduler factory
     * @param job jobDetail
     */
    public void contributeApplicationInitializer(
			OrderedConfiguration<ApplicationInitializerFilter> configuration,@Inject
			final SchedulerFactory factory,@InjectService("checkOnlineJob")
			final JobDetail job){
    	ApplicationInitializerFilter initCheckOnlinJob = new ApplicationInitializerFilter() {
			public void initializeApplication(Context context,
					ApplicationInitializer initializer) {
				Scheduler sched;
				try {
					sched = factory.getScheduler();
					sched.start();
					Trigger trigger = TriggerUtils.makeSecondlyTrigger(10);  
					trigger.setName("checkonlinejob-trigger");
					sched.scheduleJob(job, trigger);
				} catch (SchedulerException e) {
					e.printStackTrace();
				}
				initializer.initializeApplication(context);
			}
		};
		configuration.add("init-checkonline-job", initCheckOnlinJob);
    }
    /**
     * 实现对field的校验的扩展.
     * @param configuration 配置.
     * @since 0.0.2
     */
    public static void contributeFieldValidatorSource(
            MappedConfiguration<String, Validator> configuration)
    {
    	//针对email的校验
        configuration.add("email", new Email());
    }
}
